package com.thz.admin.controller;

import com.thz.system.service.cos.ICosFileService;
import com.thz.system.utils.cos.CosFileUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.apache.catalina.connector.ClientAbortException;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

/**
 * 文件访问控制器
 * 提供COS文件的代理访问服务
 * 
 * @author THZ
 * @date 2025
 */
@Slf4j
@RestController
@RequestMapping("/api/files")
@Tag(name = "文件访问", description = "COS文件代理访问接口")
public class FileController {
    
    @Autowired
    private CosFileUtils cosFileUtils;
    
    @Autowired
    private ICosFileService cosFileService;
    
    
    /**
     * 代理COS文件访问（用于预览，避免CORS和Content-Disposition问题）
     * 
     * @param url 编码后的COS URL（可选，但为空时会返回错误）
     * @param download 是否下载
     * @param request HTTP请求对象
     * @param response HTTP响应对象
     */
    @GetMapping("/proxy")
    @Operation(summary = "代理COS文件", description = "通过后端代理访问COS文件，用于预览，避免CORS问题")
    public void proxyCosFile(@RequestParam(value = "url", required = false) String url,
                             @RequestParam(value = "download", required = false, defaultValue = "false") boolean download,
                             HttpServletRequest request,
                             HttpServletResponse response) {
        try {
            // 参数验证
            if (url == null || url.trim().isEmpty()) {
                // 记录更多调试信息，帮助定位问题
                String userAgent = request.getHeader("User-Agent");
                String referer = request.getHeader("Referer");
                String queryString = request.getQueryString();
                
                // 对于无效请求，返回友好的错误信息（但不记录为错误，可能是浏览器自动请求）
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                response.setContentType("text/plain;charset=UTF-8");
                response.getWriter().write("缺少必需的参数: url");
                return;
            }
            
            // URL解码
            String decodedUrl = java.net.URLDecoder.decode(url, StandardCharsets.UTF_8);
            log.debug("代理COS文件访问: {}", decodedUrl);
            
            // 从COS下载文件
            InputStream inputStream = cosFileService.downloadFile(decodedUrl);
            
            // 获取文件名
            String filename = extractFilenameFromUrl(decodedUrl);
            
            // 设置Content-Type
            String contentType = getContentType(filename);
            response.setContentType(contentType);
            
            // 设置Content-Disposition
            if (download) {
                String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8);
                response.setHeader("Content-Disposition", 
                        "attachment; filename=\"" + encodedFilename + "\"; filename*=UTF-8''" + encodedFilename);
            } else {
                // 预览模式，设置为inline
                response.setHeader("Content-Disposition", "inline; filename=\"" + filename + "\"");
            }
            
            // 设置缓存头
            response.setHeader("Cache-Control", "public, max-age=600");
            
            // 复制流
            try (InputStream is = inputStream; OutputStream os = response.getOutputStream()) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = is.read(buffer)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
                os.flush();
            } catch (ClientAbortException cae) {
                // 前端/代理取消连接：降级为 info，避免刷错误日志
                log.info("客户端中断连接（COS代理）：{}", cae.getMessage());
                return;
            } catch (IOException io) {
                String msg = String.valueOf(io.getMessage());
                if (msg.contains("Broken pipe") || msg.contains("Connection reset")) {
                    log.info("客户端连接断开（COS代理）：{}", msg);
                    return;
                }
                throw io;
            }
            
            log.debug("COS文件代理成功: {}", decodedUrl);
            
        } catch (IllegalArgumentException e) {
            // URL解码失败
            log.warn("URL解码失败: {}", url, e);
            try {
                if (!response.isCommitted()) {
                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                    response.setContentType("text/plain;charset=UTF-8");
                    response.getWriter().write("URL格式错误: " + e.getMessage());
                }
            } catch (IOException ex) {
                log.error("写入错误响应失败", ex);
            }
        } catch (Exception e) {
            log.error("代理COS文件失败: url={}, error={}", url, e.getMessage(), e);
            try {
                if (!response.isCommitted()) {
                    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                    response.setContentType("text/plain;charset=UTF-8");
                    String errorMsg = e.getMessage();
                    if (errorMsg == null || errorMsg.isEmpty()) {
                        errorMsg = "文件访问失败，请稍后重试";
                    }
                    response.getWriter().write("文件访问失败: " + errorMsg);
                }
            } catch (IOException ex) {
                log.error("写入错误响应失败", ex);
            }
        }
    }
    
    /**
     * 代理本地文件访问（用于异步上传中的临时文件访问）
     * 
     * @param path 编码后的本地文件路径
     * @param download 是否下载
     * @param response HTTP响应对象
     */
    @GetMapping("/local")
    @Operation(summary = "代理本地文件", description = "通过后端代理访问本地文件，用于异步上传中的临时文件访问")
    public void proxyLocalFile(@RequestParam("path") String path,
                              @RequestParam(value = "download", required = false, defaultValue = "false") boolean download,
                              HttpServletResponse response) {
        try {
            // URL解码
            String decodedPath = URLDecoder.decode(path, StandardCharsets.UTF_8);
            log.debug("代理本地文件访问: {}", decodedPath);
            
            File file = new File(decodedPath);
            if (!file.exists() || !file.isFile()) {
                // log.warn("本地文件不存在: {}", decodedPath);
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                response.setContentType("text/plain;charset=UTF-8");
                response.getWriter().write("文件不存在");
                return;
            }
            
            // 获取文件名
            String filename = file.getName();
            
            // 设置Content-Type
            String contentType = getContentType(filename);
            response.setContentType(contentType);
            
            // 设置Content-Disposition
            if (download) {
                String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8);
                response.setHeader("Content-Disposition", 
                        "attachment; filename=\"" + encodedFilename + "\"; filename*=UTF-8''" + encodedFilename);
            } else {
                // 预览模式，设置为inline
                response.setHeader("Content-Disposition", "inline; filename=\"" + filename + "\"");
            }
            
            // 设置缓存头
            response.setHeader("Cache-Control", "public, max-age=600");
            
            // 设置文件大小
            response.setContentLengthLong(file.length());
            
            // 复制流
            try (FileInputStream fis = new FileInputStream(file);
                 OutputStream os = response.getOutputStream()) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
                os.flush();
            } catch (ClientAbortException cae) {
                log.info("客户端中断连接（本地文件代理）：{}", cae.getMessage());
                return;
            } catch (IOException io) {
                String msg = String.valueOf(io.getMessage());
                if (msg.contains("Broken pipe") || msg.contains("Connection reset")) {
                    log.info("客户端连接断开（本地文件代理）：{}", msg);
                    return;
                }
                throw io;
            }
            
            log.debug("本地文件代理成功: {}", decodedPath);
            
        } catch (Exception e) {
            log.error("代理本地文件失败: {}", path, e);
            try {
                if (!response.isCommitted()) {
                    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                    response.setContentType("text/plain;charset=UTF-8");
                    response.getWriter().write("文件访问失败: " + e.getMessage());
                }
            } catch (IOException ex) {
                log.error("写入错误响应失败", ex);
            }
        }
    }
    
    /**
     * 从URL中提取文件名
     */
    private String extractFilenameFromUrl(String url) {
        if (url == null || url.isEmpty()) {
            return "file";
        }
        
        // 移除查询参数
        int queryIndex = url.indexOf('?');
        if (queryIndex > 0) {
            url = url.substring(0, queryIndex);
        }
        
        // 提取最后一个/后的文件名
        int lastSlash = url.lastIndexOf('/');
        if (lastSlash >= 0 && lastSlash < url.length() - 1) {
            return url.substring(lastSlash + 1);
        }
        
        return "file";
    }
    
    /**
     * 根据文件扩展名获取Content-Type
     * 
     * @param filename 文件名
     * @return Content-Type
     */
    private String getContentType(String filename) {
        String lowercaseFilename = filename.toLowerCase();
        if (lowercaseFilename.endsWith(".gif")) {
            return "image/gif";
        } else if (lowercaseFilename.endsWith(".png")) {
            return "image/png";
        } else if (lowercaseFilename.endsWith(".jpg") || lowercaseFilename.endsWith(".jpeg")) {
            return "image/jpeg";
        } else if (lowercaseFilename.endsWith(".webp")) {
            return "image/webp";
        } else if (lowercaseFilename.endsWith(".svg")) {
            return "image/svg+xml";
        } else if (lowercaseFilename.endsWith(".mp4")) {
            return "video/mp4";
        } else {
            return "application/octet-stream";
        }
    }
}




